﻿#region Header

// Written by Tobias Lekman, 2008. For more information and contact details, see http://www.lekman.com.
// 
// File released under Microsoft Public License (Ms-PL). For full details see http://www.microsoft.com/opensource/licenses.mspx
// 
// Conditions and Limitations
// 
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// 
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
// 
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
// 
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
// 
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 

#endregion

#region Using Statements

using System;
using System.Collections.Generic;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.WebControls;

#endregion

namespace CodePlex.ExtendedLookup
{
    /// <summary>
    /// A lookup field that allows the available values to be filtered to a specific view from any site within the site collection.
    /// </summary>
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
    public class ExtendedLookupField : SPFieldLookup
    {
        #region Fields

        private static readonly Dictionary<int, bool> allowMultipleDictionary = new Dictionary<int, bool>();
        private static readonly Dictionary<int, Guid> lookupViewIdDictionary = new Dictionary<int, Guid>();
        private static readonly Dictionary<int, bool> addMissingDictionary = new Dictionary<int, bool>();
        private bool allowMultipleValues;
        private bool addMissingValue;
        private Guid lookupViewId;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the ExtendedLookupField class.
        /// </summary>
        /// <param name="fields">An <see cref="SPFieldCollection"/> object that represents the parent field collection.</param>
        /// <param name="fieldName">A string that contains the name of the field.</param>
        public ExtendedLookupField(SPFieldCollection fields,
                                   string fieldName)
            : base(fields,
                   fieldName)
        {
            Initialize();
        }

        /// <summary>
        /// Initializes a new instance of the ExtendedLookupField class.
        /// </summary>
        /// <param name="fields">An <see cref="SPFieldCollection"/> object that represents the parent field collection.</param>
        /// <param name="typeName">A string that contains the type name.</param>
        /// <param name="displayName">A string that contains the display name of the field.</param>
        public ExtendedLookupField(SPFieldCollection fields,
                                   string typeName,
                                   string displayName)
            : base(fields,
                   typeName,
                   displayName)
        {
            Initialize();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether multiple values can be used in the lookup field.
        /// </summary>
        public override bool AllowMultipleValues
        {
            get
            {
                return allowMultipleDictionary.ContainsKey(ContextId)
                           ? allowMultipleDictionary[ContextId]
                           : allowMultipleValues;
            }
            set
            {
                allowMultipleValues = value;
                SetCustomProperty("Type",
                                  value
                                      ? "ExtendedLookupMulti"
                                      : "ExtendedLookup");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool AddMissingValue
        {
            get
            {
                return addMissingValue;
            }
            set
            {
                addMissingValue = value;
                SetCustomProperty("AddMissingValue", value);
            }
        }

        /// <summary>
        /// Gets the hash code of the current context.
        /// </summary>
        public int ContextId
        {
            get
            {
                if (SPContext.Current != null)
                {
                    return SPContext.Current.GetHashCode();
                }
                return GetHashCode();
            }
        }

        /// <summary>
        /// Gets the control that is used to render the field.
        /// </summary>
        /// <returns>
        /// An <see cref="T:Microsoft.SharePoint.WebControls.BaseFieldControl"></see> object that represents the rendering control.
        /// </returns>
        public override BaseFieldControl FieldRenderingControl
        {
            get
            {
                BaseFieldControl control;
                if (!CountRelated)
                {
                    if (!AllowMultipleValues)
                    {
                        control = new ExtendedLookupFieldControl();
                    }
                    else
                    {
                        control = new ExtendedMultipleLookupFieldControl();
                    }
                }
                else
                {
                    control = new ComputedField();
                }
                control.FieldName = InternalName;
                return control;
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="Guid"/> of the view to use within the list for the lookup.
        /// </summary>
        public Guid LookupViewId
        {
            get
            {
                return lookupViewIdDictionary.ContainsKey(ContextId)
                           ? lookupViewIdDictionary[ContextId]
                           : lookupViewId;
            }
            set
            {
                lookupViewId = value;
            }
        }

        #endregion

        #region Overrides of SPField

        /// <summary>
        /// Occurs after a field is added.
        /// </summary>
        /// <param name="op">An <see cref="SPAddFieldOptions"/> value that specifies an option that is implemented after the field is created.</param>
        public override void OnAdded(SPAddFieldOptions op)
        {
            base.OnAdded(op);
            Update();
        }

        /// <summary>
        /// Saves the value of the <see cref="LookupViewId"/> to the underlying field instance.
        /// </summary>
        public override void Update()
        {
            SetCustomProperty("LookupViewId",
                              LookupViewId.ToString("B"));
            SetCustomProperty("AllowMultipleValues",
                              AllowMultipleValues
                                  ? "1"
                                  : "0");
            SetCustomProperty("Type",
                                  AllowMultipleValues
                                      ? "ExtendedLookupMulti"
                                      : "ExtendedLookup");
            SetCustomProperty("AddMissingValue", AddMissingValue ? "1" : "0");
            base.Update();

            if (lookupViewIdDictionary.ContainsKey(ContextId))
            {
                lookupViewIdDictionary.Remove(ContextId);
            }
            if (allowMultipleDictionary.ContainsKey(ContextId))
            {
                allowMultipleDictionary.Remove(ContextId);
            }
            if (addMissingDictionary.ContainsKey(ContextId))
            {
                addMissingDictionary.Remove(ContextId);
            }
        }

        /// <summary>
        /// This method ensures that a value is provided if the field is mandatory.
        /// </summary>
        /// <param name="value">The string to validate against the control.</param>
        /// <returns>A validation message.</returns>
        public override string GetValidatedString(object value)
        {
            if (Required)
            {
                if (value == null ||
                    string.IsNullOrEmpty(value.ToString()))
                {
                    throw new SPFieldValidationException("Please fill in this mandatory field");
                }
                return value.ToString();
            }
            return value != null
                       ? value.ToString()
                       : null;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Saves the custom values of the control
        /// </summary>
        /// <param name="viewId">The ID of the view to filter data by.</param>
        /// <param name="allowMultiple">Specifies whether multiple values can be used in the lookup field.</param>
        /// <param name="addMissing"></param>
        public void Save(Guid viewId, bool allowMultiple, bool addMissing)
        {
            lookupViewIdDictionary[ContextId] = viewId;
            allowMultipleDictionary[ContextId] = allowMultiple;
            addMissingDictionary[ContextId] = addMissing;
        }

        /// <summary>
        /// Gets the persisted value for <see cref="LookupViewId"/> from the underlying field instance.
        /// </summary>
        private void Initialize()
        {
            if (GetCustomProperty("LookupViewId") != null)
            {
                LookupViewId = new Guid(GetCustomProperty("LookupViewId").ToString());
            }
            if (GetCustomProperty("AllowMultipleValues") != null)
            {
                AllowMultipleValues = bool.Parse(GetCustomProperty("AllowMultipleValues").ToString());
            }
            if (GetCustomProperty("AddMissingValue") != null)
            {
                AddMissingValue = bool.Parse(GetCustomProperty("AddMissingValue").ToString());
            }
        }

        #endregion
    }
}